「在我的電腦上沒問題啊?」
這個程式設計師的迷因回答實在太經典了。

很多 Windows C++ 開發者都遇過這個瞬間:編譯成功,按下 F5,程式可以正常跑。然後你興沖沖地把 .exe 傳給同事,對方點下後卻看到「找不到 xxx.dll,程式無法啟動」。
這是函式庫使用時,很常碰到的部署問題。
用 Conan 的人平常不太會遇到這個問題,是因為 ConanCenter 上大部分的套件預設都是靜態連結(shared=False),所以不需要找動態函式庫(.dll/.so/.dylib)。但今天的故事,要進入一個情境:你刻意選擇用動態連結函式庫。
比如說,程式使用了 OpenSSL 加密函式庫,就是一個典型的例子。它跟安全性有關,三不五時會出現漏洞公告。如果是靜態連結,要修正這些漏洞,每次都要重新編譯整個專案,再重新發布整個程式。但是如果是用 DLL 呢?只要換掉一個 DLL 就能修好漏洞了。
所以,只要用到動態函式庫,就要解決程式啟動時怎麼找 DLL 的問題。今天就來看看 Conan 套件管理器在這方面能幫上什麼忙。
我們這裡用一個 Windows 小程式作範例。
這個程式使用 Conan 管理套件,導入了 OpenSSL 加密函式庫。
conanfile.txt 中,我們想用動態函式庫,所以在 [options] 區段設定 shared=True:
[requires]
openssl/4.0.3
[options]
openssl/*:shared=True
[generators]
CMakeDeps
CMakeToolchain
CMakeLists.txt
cmake_minimum_required(VERSION 3.23)
project(my_app C)
find_package(OpenSSL REQUIRED)
add_executable(my_app main.c)
target_link_libraries(my_app OpenSSL::Crypto)
install(TARGETS my_app)
main.c
#include <stdio.h>
#include <openssl/crypto.h>
int main(void) {
printf("%s\n", OpenSSL_version(OPENSSL_VERSION));
return 0;
}
安裝依賴並編譯專案:
conan install . -of=build/Release -s build_type=Release --build=missing
cmake --preset conan-default
cmake --build --preset conan-release
ConanCenter 上面有預編譯好的 OpenSSL 4.0.3 二進位檔,可直接下載,不用自己編譯。
專案編譯很順利,但是接下來執行 build\Release\Release\my_app.exe 時,程式馬上爆出錯誤,錯誤碼是:0xC0000135,意思是 Windows 找不到需要的 OpenSSL DLL。
所以 OpenSSL 的 DLL 檔案到底在哪裡呀?原來 OpenSSL 的兩個動態函式庫都藏在 Conan 的本機快取裡面,在 OpenSSL 套件的 bin 目錄下。Windows 找 DLL 時,主要看執行檔所在的資料夾、系統目錄和 PATH,Conan 快取不在這些地方,當然找不到。
如果你是用 Visual Studio 按 F5 執行,倒是可以順利跑起來,因為 Conan 產生的
conan_toolchain.cmake會把套件的bin目錄設定進 Visual Studio 的偵錯環境裡。不過這只有在用 CMake 產生 Visual Studio 專案時才有效。
怎麼辦呢?
呼叫 conan install 指令之後,其實輸出目錄下會出現一個 conanrun.bat 批次檔,它是 Conan 預設啟用的 VirtualRunEnv generator 產生的。(macOS/Linux 上是 conanrun.sh,要用 source 執行,設定的是 LD_LIBRARY_PATH/DYLD_LIBRARY_PATH。)
只要在執行 exe 之前,在命令列裡面,先執行它,再啟動程式:
build\Release\conanrun.bat
build\Release\Release\my_app.exe
程式就能順利跑起來了,印出 OpenSSL 4.0.3 29 Sep 2026。找不到 DLL 的錯誤消失了。
conanrun.bat 做的事情,就是把各套件的 bin 目錄加入環境變數 PATH 裡面,只在當前命令列視窗有效。
這種方式適合本機開發、測試、或者 CI/CD 環境。
但是終端使用者,肯定沒辦法執行 conanrun.bat 呀,所以這不是最終的部署方案。但是開發用,夠了。
記得在 Windows 上面,debug 和 release 要各自安裝到不同的輸出目錄,這樣
conanrun.bat不會互相覆蓋,而且可以避免 debug 程式載入 release DLL 這類可能導致很難排查的 Heap Corruption 錯誤。
這個 conanrun.bat (conanrun.sh) 是 VirtualRunEnv 產生的,有興趣可用此關鍵字往下查。
runtime_deployVirtualRunEnv 讓程式去本機快取裡面找 DLL,但是真正軟體出貨時,應該要反過來,把檔案從快取複製出來,放在執行檔的旁邊,跟著一起打包,這樣才是正解。
Conan 內建的 runtime_deploy,就是專門複製執行期需要的檔案:
> conan install . -of=build/Release -s build_type=Release --deployer=runtime_deploy --deployer-folder=dist/Release
runtime_deploy: WARN: This deployer is experimental and subject to change.
runtime_deploy: Copied 3 files from openssl/4.0.3
這裡我們在安裝指令裡面加了兩個參數:
bin 目錄裡的檔案,全部複製到指定目錄。跑完之後,dist\Release 目錄裡面就會有三個檔案囉:
dist\Release\
├── libcrypto-4-x64.dll
├── libssl-4-x64.dll
└── openssl.exe
要注意,這個指令只會複製相依套件的檔案,不會複製我們自己的程式。要讓 my_app.exe 跟 DLL 進同一個目錄,要嘛自己指定專案編譯的輸出位置,要嘛自己把 exe 複製進去。
在 mac/Linux 上,
runtime_deploy一樣會把.so/.dylib平鋪複製出來,但執行檔預設不會到自己旁邊找函式庫,要在 CMake 把 RPATH 設成$ORIGIN(macOS 是@loader_path),這一步 Conan 不會幫你。
目前
runtime_deploy仍是實驗功能,之後用法可能會變。
如果你的程式本身也做成 Conan 套件,用 Conan 來編譯的話,就可以在 recipe 裡覆寫 deploy() 方法,完全自訂部署的行為。
這時要把原本的 conanfile.txt 換成下面這份 conanfile.py(recipe 的寫法可以回頭參考 D15):
import os
from conan import ConanFile
from conan.tools.cmake import CMake, cmake_layout
from conan.tools.files import copy
class MyAppConan(ConanFile):
name = "my_app"
version = "1.0"
settings = "os", "compiler", "build_type", "arch"
generators = "CMakeDeps", "CMakeToolchain"
exports_sources = "CMakeLists.txt", "main.c"
default_options = {"openssl/*:shared": True}
def requirements(self):
self.requires("openssl/4.0.3")
def layout(self):
cmake_layout(self)
def build(self):
cmake = CMake(self)
cmake.configure()
cmake.build()
def package(self):
cmake = CMake(self)
cmake.install()
def deploy(self):
copy(self, "*.exe", os.path.join(self.package_folder, "bin"), self.deploy_folder)
for dep in self.dependencies.host.values():
for bindir in dep.cpp_info.bindirs:
copy(self, "*.dll", bindir, self.deploy_folder)
在專案目錄下執行以下指令:
conan create . --build=missing
conan install --requires=my_app/1.0 --deployer-package="my_app/*" --deployer-folder=dist
接著去看 dist 目錄,就會發現目錄底下已經有 my_app.exe、libcrypto-4-x64.dll、libssl-4-x64.dll,直接執行就能印出 OpenSSL 版本了。
要注意寫法,只寫
--deployer-package=my_app不行,而且 Conan 不會報錯。要寫--deployer-package="my_app/*"或--deployer-package=my_app/1.0。
Conan 在部署上能幫的忙:
VirtualRunEnv 產生的 conanrun.bat,就可以讓程式找到 DLLruntime_deploy 把 DLL 複製出來deploy() 可以完全自己決定如何部署